package org.japo.java.entities;

import java.io.Serializable;

/**
 *
 * @author José A. Pacheco Ondoño - joanpaon@gmail.com
 */
public class Nif implements Serializable {

    // Limites DNI
    public final static int DNI_MIN = 0;
    public final static int DNI_MAX = 99999999;

    // Secuencia de letras
    public static final String LETRAS = "TRWAGMYFPDXBNJZSQVHLCKE";

    // Atributos de la clase
    private int dni;

    // Constructor predeterminado
    public Nif() {
        this.dni = DNI_MIN;
    }

    // Constructor parametrizado
    public Nif(int dni) {
        if (validarDni(dni)) {
            this.dni = dni;
        } else {
            this.dni = DNI_MIN;
        }
    }

    // --- Inicio encapsulación
    //
    public int getDni() {
        return dni;
    }

    public void setDni(int dni) {
        if (validarDni(dni)) {
            this.dni = dni;
        }
    }

    // --- Letra no tiene set
    //
    public char getLetra() {
        return calcularLetraNif(dni);
    }

    // --- Fin encapsulación
    //
    public static char calcularLetraNif(int dni) {
        return LETRAS.charAt(dni % LETRAS.length());
    }

    public static int extraerDni(String nif) {
        // Almacen del DNI extraido
        int dni;

        // Se asume NIF en formato NNNNNNNNL
        try {
            // Texto que representa el DNI
            String sDni;

            // Posición guión
            int posGuion = nif.indexOf('-');

            // Análisis formato NIF
            if (posGuion > -1) {    // Se asume NIF en formato NNNNNNNN-L
                sDni = nif.substring(0, posGuion);
            } else {                // Se asume NIF en formato NNNNNNNNL
                sDni = nif.substring(0, nif.length() - 1);
            }

            // Convierte el texto a entero
            dni = Integer.parseInt(sDni);

            // Valida el DNI
            if (validarDni(dni) == false) {
                throw new Exception("NIF erróneo");
            }
        } catch (Exception e) {
            // Valor de DNI para NIF erróneo
            dni = 0;
        }

        // Devuelve el DNI obtenido
        return dni;
    }

    public static char extraerLetra(String nif) {
        // Posición guión
        int posGuion = nif.indexOf('-');

        // Letra del NIF
        char letra;

        // Análisis formato NIF
        if (posGuion > -1) {            // Se asume NIF en formato NNNNNNNN-L
            letra = nif.charAt(posGuion + 1);
        } else if (nif.length() > 0) {  // Se asume NIF en formato NNNNNNNNL
            letra = nif.charAt(nif.length() - 1);
        } else {                        // Format de NIF incorecto
            letra = '?';
        }

        // Convierte la letra a mayúscula
        letra = Character.toUpperCase(letra);

        // Comprueba si la letra está en la lista
        if (LETRAS.indexOf(letra) == -1) {
            // Letra desconocida > asterisco
            letra = '?';
        }

        // Devuelve la letra extraida
        return letra;
    }

    public void mostrarNifConsola() {
        System.out.println("NIF: " + toString());
    }

    public static Nif convertirNif(String nif) {
        // Obtiene el número de DNI
        int dni = extraerDni(nif);

        // Devuelve el objeto generado
        return new Nif(dni);
    }

    public static boolean validarDni(int dni) {
        return dni >= DNI_MIN && dni <= DNI_MAX;
    }

    public static boolean validarNif(String nif) {
        // Semáforos de análisis
        boolean dniOK;
        boolean letraOK;

        // Análisis
        try {
            // Análisis DNI
            String sDni = nif.substring(0, nif.length() - 1);
            int dni = Integer.parseInt(sDni);
            dniOK = validarDni(dni);

            // Análisis letra
            char letra = nif.charAt(nif.length() - 1);
            letra = Character.toUpperCase(letra);
            letraOK = letra == calcularLetraNif(dni);
        } catch (Exception e) {
            dniOK = false;
            letraOK = false;
        }

        // Resultado del análisis
        return dniOK && letraOK;
    }

    // --- Inicio de sobrecarga de métodos
    //
    @Override
    public String toString() {
        return String.format("%08d%c", dni, calcularLetraNif(dni));
    }

    @Override
    public boolean equals(Object o) {
        // Semaforo de resultado
        boolean testOK;

        // Análisis del objeto
        if (o == null) {
            testOK = false;
        } else if (!(o instanceof Nif)) {
            testOK = false;
        } else {
            Nif nif = (Nif) o;
            testOK = dni == nif.dni;
        }

        // Devolución del resultado
        return testOK;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 53 * hash + this.dni;
        return hash;
    }

    // --- Fin de sobrecarga de métodos
    //
}
